package common

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

// ReentrantLock 重入锁
type ReentrantLock struct {
	lock      *sync.Mutex
	cond      *sync.Cond
	recursion int32
	host      int64
}

func NewReentrantLock() sync.Locker {
	res := &ReentrantLock{
		lock:      new(sync.Mutex),
		recursion: 0,
		host:      0,
	}
	res.cond = sync.NewCond(res.lock)
	return res
}

func (rt *ReentrantLock) Lock() {
	id := GetGoroutineID()
	rt.lock.Lock()
	defer rt.lock.Unlock()

	if rt.host == id {
		rt.recursion++
		return
	}

	for rt.recursion != 0 {
		rt.cond.Wait()
	}
	rt.host = id
	rt.recursion = 1
}
func (rt *ReentrantLock) Unlock() {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	if rt.recursion == 0 || rt.host != GetGoroutineID() {
		panic(fmt.Sprintf("the wrong call host: (%d); current_id: %d; recursion: %d", rt.host, GetGoroutineID(), rt.recursion))
	}

	rt.recursion--
	if rt.recursion == 0 {
		rt.cond.Signal()
	}
}

func GetGoroutineID() int64 {
	var buf [64]byte
	// 获取栈信息
	n := runtime.Stack(buf[:], false)
	// 抽取id
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine"))[0]
	// 转为64位整数
	id, _ := strconv.Atoi(idField)
	return int64(id)
}
